﻿using System;
using System.CodeDom.Compiler;
using System.Text;

namespace CodeContrib
{
	public class ExpressionEvaluator
	{
		private const string _className = "__Evaluator";
		private const string _methodName = "Evaluate";
		private const string _namespace = "CodeContrib";

		public object Evaluate(string expression)
		{
			var provider = CodeDomProvider.CreateProvider("CSharp");

			var parameters = new CompilerParameters();
			parameters.ReferencedAssemblies.Add("System.dll");
			parameters.ReferencedAssemblies.Add("System.Data.dll");
			parameters.ReferencedAssemblies.Add("System.Xml.dll");
			parameters.GenerateExecutable = false;
			parameters.GenerateInMemory = true;

			var code = new StringBuilder();
			code.Append("using System;");
			code.Append("using System.Data;");
			code.Append("using System.Data.SqlClient;");
			code.Append("using System.Data.OleDb;");
			code.Append("using System.Xml;");
			code.AppendFormat("namespace {0}", _namespace);
			code.Append("{");
			code.AppendFormat("public class {0}", _className);
			code.Append("{");
			code.AppendFormat("public object {0}()", _methodName);
			code.Append("{");
			code.AppendFormat("return ({0});", expression);
			code.Append("}");
			code.Append("}");
			code.Append("}");

			var results = provider.CompileAssemblyFromSource(parameters, code.ToString());

			if (results.Errors.HasErrors)
			{
				var error = results.Errors[0];

				// TODO Switch the error number.

				throw new ApplicationException(error.ErrorText);
			}

			var assembly = results.CompiledAssembly;

			var compiled = assembly.CreateInstance(string.Format("{0}.{1}", _namespace, _className));

			var method = compiled.GetType().GetMethod(_methodName);

			return method.Invoke(compiled, null);
		}
	}
}